/* eslint-disable no-unused-vars */
/* eslint-disable no-debugger */
import namespace from "./namespace";

import shortid from "shortid";
import EventEmitter from "eventemitter3";
import extend from "lodash/extend";
import ArrayList from "./util/ArrayList";
import isPlainObject from "lodash/isPlainObject";
import isFunction from "lodash/isFunction";
import JSONUtil from "./util/JSONUtil";
import Snap from "snapsvg";

/**
 * 图形基类
 */
class Figure extends EventEmitter {
  NAME = `${namespace.namespace}.Figure`;

  constructor(attr, setter = {}, getter = {}) {
    super();
    this.setterWhitelist = extend(
      {
        id: this.setId,
        x: this.setX,
        y: this.setY,
        width: this.setWidth,
        height: this.setHeight,
        userData: this.setUserData,
        resizeable: this.setResizeable,
        selectable: this.setSelectable,
        angle: this.setRotationAngle,
        alpha: this.setAlpha,
        opacity: this.setAlpha,
        dashArray: this.setDashArray,
        keepAspectRatio: this.setKeepAspectRatio,
        className: this.setClassName
      },
      setter
    );

    this.getterWhitelist = extend(
      {
        id: this.getId,
        angle: this.getRotationAngle,
        x: this.getX,
        y: this.getY,
        width: this.getWidth,
        height: this.getHeight,
        resizeable: this.isResizeable,
        selectable: this.isSelectable,
        opacity: this.getAlpha
      },
      getter
    );

    this.id = shortid();
    this.minHeight = 2;
    this.minWidth = 2;
    this.x = 0;
    this.y = 0;
    // 拖拽是记录原坐标
    this.ox = 0;
    this.oy = 0;

    this.alpha = 1.0;
    this.dashArray = null;
    this.rotationAngle = 0;
    this.className = "";

    this.width = this.getMinWidth();
    this.height = this.getMinHeight();

    this.canvas = null; // 画板
    this.shape = null; // snap element

    this.children = new ArrayList();

    this.rows = new ArrayList(); // 座位排列信息 不同平面,子父级

    this.selectionHandles = new ArrayList(); // 保存放大缩小四个角以及选中框的图形

    this.policys = new ArrayList();

    this.parent = null; // 父级图形

    // 图形所携带数据 JSON read/write
    this.userData = null;

    //behavior
    this.selectable = true; // 是否可以被选中
    this.resizeable = true; // 是否可以缩放
    this.repaintBlocked = false; // 是否重绘
    this.draggable = true; //允许拖拽

    this.isHandleFigures = false; //默认图形不是交互动作类图形

    this.keepAspectRatio = false; // 是否保持尺寸比例

    // 计算重复属性
    this.lastAppliedAttributes = {};

    // 框选交互
    this.installPolicy(new namespace.policy.figure.RectangleSelectionPolicy());

    // this.ins
    this.attr(attr);
  }

  getId() {
    return this.id;
  }

  setId(id) {
    this.id = id;
    return this;
  }

  getCanvas() {
    return this.canvas;
  }

  setCanvas(canvas) {
    this.canvas = canvas;

    if (canvas === null && this.shape !== null) {
      this.shape.remove();
      this.shape = null;
    }

    if (this.canvas !== null) {
      this.getShapeElement();
    }

    this.lastAppliedAttributes = {};

    return this;
  }

  createShapeElement() {
    throw `Inherited class [${this.NAME}] must override the abstract method createShapeElement`;
  }

  getShapeElement() {
    if (this.shape !== null) {
      return this.shape;
    }

    this.shape = this.createShapeElement();

    return this.shape;
  }

  // eslint-disable-next-line no-unused-vars
  attr(name, value) {
    if (isPlainObject(name)) {
      // console.log(`${this.NAME}'s(${this.id}) attr:`, JSON.stringify(name));
      for (let key in name) {
        let func = this.setterWhitelist[key];
        let param = name[key];
        if (func && param !== undefined) {
          func.call(this, param);
        } else if (isFunction(name[key])) {
          this[key] = param.bind(this);
        }
      }
    }
  }

  // 重绘
  repaint(attributes = {}) {
    if (this.repaintBlocked === true || this.shape === null) {
      return this;
    }

    attributes.opacity = this.alpha;

    JSONUtil.ensureDefault(attributes, "class", this.className);

    JSONUtil.ensureDefault(attributes, "fill-opacity", this.alpha);
    this.dashArray &&
      JSONUtil.ensureDefault(attributes, "stroke-dasharray", this.dashArray);

    // 避免重复设置属性
    attributes = JSONUtil.flatDiff(attributes, this.lastAppliedAttributes);

    this.lastAppliedAttributes = attributes;

    if (Object.getOwnPropertyNames(attributes).length > 0) {
      this.emit("attr");
      this.shape.attr(attributes);
    }

    this.applyTransformation();

    return this;
  }

  // @interface
  applyTransformation() {
    return this;
  }

  // 尝试改成订阅制 / 尝试矩阵来操作移动,放大缩小,之后再次更新属性.
  setPosition(x, y) {
    if (x instanceof namespace.geo.Point) {
      this.x = x.x;
      this.y = x.y;
    } else {
      this.x = x;
      this.y = y;
    }

    this.policys.each((i, e) => {
      if (e instanceof namespace.policy.figure.DragDropPolicy) {
        let newPos = e.adjustPosition(this, this.x, this.y);
        this.x = newPos.x;
        this.y = newPos.y;
      }
    });

    this.repaint();

    // 尝试改成订阅制 / 尝试矩阵来操作移动,放大缩小,之后再次更新属性.
    this.policys.each((i, e) => {
      if (e instanceof namespace.policy.figure.DragDropPolicy) {
        e.move(this.canvas, this);
      }
    });
  }

  getPosition() {
    return new namespace.geo.Point(this.getX(), this.getY());
  }

  setClassName(className) {
    this.className = className === null ? null : className.trim();

    if (this.shape === null) {
      this.repaint();
      return this;
    }

    if (this.className === null) {
      this.shape.node.removeAttribute("class");
    } else {
      this.shape.node.setAttribute("class", this.className);
    }

    return this;
  }

  hasClass(className) {
    if (this.className === null) {
      return false;
    }
    return new RegExp(" " + className.trim() + " ").test(
      " " + this.className + " "
    );
  }

  addClassName(className) {
    className = className.trim();
    if (!this.hasClass(className)) {
      if (this.className === null) {
        this.setCssClass(className);
      } else {
        this.setCssClass(this.className + " " + className);
      }
    }
    return this;
  }

  setX(x) {
    this.setPosition(parseFloat(x), this.y);
    return this;
  }

  getX() {
    return this.x;
  }

  setY(y) {
    this.setPosition(this.x, parseFloat(y));
    return this;
  }

  getY() {
    return this.y;
  }

  getWidth() {
    return this.width;
  }

  getHeight() {
    return this.height;
  }

  setWidth(w) {
    this.setDimension(w, this.getHeight());
    return this;
  }

  setHeight(h) {
    this.setDimension(this.getWidth(), h);
    return this;
  }

  getMinWidth() {
    return this.minWidth;
  }

  setMinWidth(w) {
    this.minWidth = parseFloat(w);

    this.setWidth(this.getWidth());

    return this;
  }

  getMinHeight() {
    return this.minHeight;
  }

  setMinHeight(h) {
    this.minHeight = parseFloat(h);
    this.setHeight(this.getHeight());

    return this;
  }

  setDimension(w, h) {
    // 最小值比对
    w = Math.max(this.getMinWidth(), w);
    h = Math.max(this.getMinHeight(), h);

    if (this.width === w && this.height === h) {
      return this;
    }

    if (this.keepAspectRatio) {
      if (w >= this.getMinWidth()) {
        h = this.getHeight() * (w / this.getWidth());
        if (h >= this.getMinHeight()) {
          this.width = w;
          this.height = h;
        }
      }
    } else {
      this.width = w;
      this.height = h;
    }

    this.repaint();

    // 移动放大缩小框
    this.policys.each((i, e) => {
      if (e instanceof namespace.policy.figure.DragDropPolicy) {
        e.move(this.canvas, this);
      }
    });

    return this;
  }

  getAbsoluteX() {
    if (!this.parent) {
      return this.getX();
    }

    return this.getX() + this.parent.getAbsoluteX();
  }

  getAbsoluteY() {
    if (!this.parent) {
      return this.getY();
    }
    return this.getY() + this.parent.getAbsoluteY();
  }

  setAlpha(percent) {
    percent = Math.min(1, Math.max(0, parseFloat(percent)));
    if (percent === this.alpha) {
      return this;
    }

    this.alpha = percent;
    this.repaint();

    return this;
  }

  setDashArray(v) {
    this.dashArray = v;
    this.repaint();
    return this;
  }

  getAlpha() {
    return this.alpha;
  }

  setRotationAngle(angle) {
    this.rotationAngle = angle;

    this.repaint();

    this.policys.each((i, e) => {
      if (e instanceof namespace.policy.figure.DragDropPolicy) {
        e.move(this.canvas, this);
      }
    });

    return this;
  }

  getRotationAngle() {
    return this.rotationAngle;
  }

  setKeepAspectRatio(flag) {
    this.keepAspectRatio = flag;

    return this;
  }

  getKeepAspectRatio() {
    return this.keepAspectRatio;
  }

  setUserData(data) {
    this.userData = data;
  }

  getUserData() {
    return this.userData;
  }

  isResizeable() {
    return this.resizeable;
  }

  setResizeable(flag) {
    this.resizeable = !!flag;
    return this;
  }

  setSelectable(flag) {
    this.selectable = !!flag;
    return this;
  }

  isSelectable() {
    return this.selectable;
  }

  setDraggable(flag) {
    this.draggable = !!flag;

    return this;
  }

  isDraggable() {
    return this.draggable;
  }

  // 碰转检测
  hitTest(iX, iY) {
    var iX2 = this.x + this.getWidth();
    var iY2 = this.y + this.getHeight();
    return iX >= this.x && iX <= iX2 && iY >= this.y && iY <= iY2;
  }

  getBoundingBox() {
    return new namespace.geo.Rectangle(
      this.getAbsoluteX(),
      this.getAbsoluteY(),
      this.getWidth(),
      this.getHeight()
    );
  }

  // 图形框选中
  select(canvas) {
    this.policys.each((i, policy) => {
      if (policy instanceof namespace.policy.figure.SelectionPolicy) {
        policy.onSelect(canvas, this);
      }
    });
    this.canvas && this.canvas.getSelection().add(this);
    return this;
  }

  figureScale(val) {
    let m = new Snap.Matrix();
    m.scale(
      val / 100,
      val / 100,
      this.x + this.width / 2,
      this.y + this.height / 2
    );
    // 缩放
    this.shape.transform(m);
  }

  // 图形移除框选
  unselect(canvas) {
    this.policys.each((i, policy) => {
      if (policy instanceof namespace.policy.figure.SelectionPolicy) {
        policy.onUnselect(canvas, this);
      }
    });
  }

  installPolicy(policy) {
    const _constructor = namespace.policy.figure.SelectionPolicy;

    // 框选状态策略 每个图形仅有一种
    if (policy instanceof _constructor) {
      this.policys.filter(p => {
        let isSelectionPolicy = p instanceof _constructor;
        isSelectionPolicy && p.onUninstall(this);
        return !isSelectionPolicy;
      });
    }

    policy.onInstall(this);
    this.policys.add(policy);
  }

  onDragStart(x, y) {
    // 记录原坐标
    this.ox = this.getX();
    this.oy = this.getY();

    // this.oMatrix = this.shape.transform().localMatrix;

    this.policys.each((i, e) => {
      if (e instanceof namespace.policy.figure.DragDropPolicy) {
        e.onDragStart(this.canvas, this, x, y);
      }
    });
  }

  onDrag(dx, dy, dx2, dy2, event) {
    this.policys.each((i, e) => {
      if (e instanceof namespace.policy.figure.DragDropPolicy) {
        let newPos = e.adjustPosition(this, this.ox + dx, this.oy + dy);
        if (newPos) {
          dx = newPos.x - this.ox;
          dy = newPos.y - this.oy;
        }
      }
    });

    let newPos = new namespace.geo.Point(this.ox + dx, this.oy + dy);
    this.setPosition(newPos);

    this.policys.each((i, e) => {
      if (e instanceof namespace.policy.figure.DragDropPolicy) {
        e.onDrag(this.canvas, this, dx, dy, dx2, dy2, event);
      }
    });
  }

  onDragEnd(x, y) {
    this.policys.each((i, e) => {
      if (e instanceof namespace.policy.figure.DragDropPolicy) {
        e.onDragEnd(this.canvas, this, x, y);
      }
    });
  }

  /**
   * 持久化属性
   * @param {*} figure
   */
  setPersistentAttrs(figure) {
    this.id = figure.id;

    this.setPosition(parseFloat(figure.x), parseFloat(figure.y));

    if (typeof figure.width !== "undefined") {
      this.width = parseFloat(figure.width);
    }

    if (typeof figure.height !== "undefined") {
      this.height = parseFloat(figure.height);
    }

    if (typeof figure.userData !== "undefined") {
      this.userData = figure.userData;
    }

    if (typeof figure.alpha !== "undefined") {
      this.setAlpha(parseFloat(figure.alpha));
    }

    if (typeof figure.angle !== "undefined") {
      this.rotationAngle = parseFloat(figure.angle);
    }

    if (typeof figure.className !== "undefined") {
      this.className = figure.className;
    }

    return this;
  }

  /**
   * 持久化属性
   */
  getPersistentAttrs() {
    return {
      type: this.NAME,
      id: this.id,
      x: this.getX(),
      y: this.getY(),
      width: this.width,
      height: this.height,
      alpha: this.alpha,
      angle: this.rotationAngle,
      className: this.className,
      userData: extend({}, this.userData)
    };
  }

  /**
   * 图形复制
   */
  clone() {
    let clone = eval("new " + this.NAME + "();");
    let initialId = clone.id;
    clone.setPersistentAttrs(this.getPersistentAttrs());
    clone.id = initialId;
    return clone;
  }
}

namespace.Figure = Figure;
